#include <iostream>
#include <iomanip>

#include <windows.h>

#pragma pack(push, 1)
struct gdt_t
{
	UINT16 size;
	UINT32 address;
};

struct gdt_entry_t
{
   UINT16 limit_low;           // The lower 16 bits of the limit.
   UINT16 base_low;            // The lower 16 bits of the base.
   UINT8  base_middle;         // The next 8 bits of the base.
   UINT8  access;              // Access flags, determine what ring this segment can be used in.
   UINT8  granularity;
   UINT8  base_high;           // The last 8 bits of the base.
};
#pragma pack(pop)

#define printLdtFor(thread, selector) \
	std::cout << "print ldt for "#selector"(" << selector << ")" << std::endl; \
	printLdt(thread, selector);

#define value(v) #v"=" << v << std::endl

void printLdt(HANDLE thread, DWORD selector) {
	LDT_ENTRY ldt = {};
	if (GetThreadSelectorEntry(thread, selector, &ldt) == FALSE) {
		std::cout << "Can not read ldt" << std::endl;
		return;
	}

	std::cout
		<< value(ldt.LimitLow)
		<< value(ldt.BaseLow)
		<< value(ldt.HighWord.Bits.BaseMid)
		<< value(ldt.HighWord.Bits.BaseHi)
		<< value(ldt.HighWord.Bits.LimitHi)
		<< value(ldt.HighWord.Bits.Type)
		<< value(ldt.HighWord.Bits.Dpl)
		<< value(ldt.HighWord.Bits.Pres)
		<< value(ldt.HighWord.Bits.Sys)
		<< value(ldt.HighWord.Bits.Default_Big)
		<< value(ldt.HighWord.Bits.Granularity)
//		<< value(ldt.HighWord.Bits.Reserved_0)
		<< std::endl;
}

void main() {
	CONTEXT ctx = {};
	ctx.ContextFlags = CONTEXT_ALL;

	HANDLE curThread = GetCurrentThread();
	GetThreadContext(curThread, &ctx);

	printLdtFor(curThread, ctx.SegDs);
	printLdtFor(curThread, ctx.SegEs);
	printLdtFor(curThread, ctx.SegFs);
	printLdtFor(curThread, ctx.SegGs);

	UINT16 ldt;
	gdt_t gdt = {};
	__asm {
		sgdt gdt
		sldt ldt
	}

	std::cout << value(ldt)
		<< std::endl;

	std::cout << value(gdt.size)
		<< std::hex << std::showbase << value(gdt.address)
		<< std::endl;

//	gdt_entry_t* entry = (gdt_entry_t*) gdt.address;

//	std::cout << value(entry->base_low)
//			<< value(entry->base_high)
//			<< std::endl;

	system("pause");
}